package com.example.springboot.service;

import com.example.springboot.bean.Employee;
import com.example.springboot.mapper.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;

/**
 * 服务层
 *
 * @Author:ChenZhangKun
 * @Date: 2020/10/21 20:31
 */
@Service
@CacheConfig(cacheNames = "emp",cacheManager = "empCacheManager") // 抽取缓存的公共配置
public class EmployeeService {
    @Autowired
    private EmployeeMapper employeeMapper;

    /**
     * 将方法的结果进行缓存，以后查询相同数据时候会缓存起来
     * cacheManager管理多个缓存组件，对缓存的crud操作是由各个缓存组件来完成的，每个缓存有自己的名字
     * 几个属性：
     * cacheName/values ->指定缓存组件的名字 z指定将方法的缓存结果放在哪个缓存中
     * key:缓存所用的key,可以自己指定，默认是方法的参数值
     * 可以自己辨析的Spel表达式
     * keyGenerator:key的生成器，可以自己指定key的生成器组件的id,但是只能2选1
     * cacheManager：指定缓存管理器，或者cacheResolver指定获取解析器
     * condition:指定符合情况 条件才缓存
     * unless:否定缓存，为true时就不会缓存，除非的意思 unless="#result==null"
     * 原理：
     * 1 自动配置类：
     * org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
     * 2 缓存配置类
     * org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
     * org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration
     * org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
     * org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
     * org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
     * org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
     * org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
     * org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
     * org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration  ->默认生效的缓存组件
     * 可以获取或创建ConcurrentMapCacheManager->数据保存在ConcurrentHashMap中（开发中使用缓存中间件->redis）
     * 运行流程：@Cacheable为例
     * 1 方法运行前，先去查询组件cache（缓存组件） 如果没有则会进行创建
     * 2 去cache中查找缓存内容，使用一个key，默认是方法参数->id
     * key是按照某种策略来生成的，默认是keyGenerator中的simpleKeyGenerator来生成的
     * simpleKeyGenerator的生成key的策略：
     * 如果没有参数：key=new SimpleKey();
     * 如果有一个参数：key=参数值
     * 如果有多个参数：key=new SimpleKey(params)
     * 3 没有找到缓存则调用目标方法
     * 4 将目标方法的返回结果放在缓存中
     * 总结：先找缓存如果没有再运行方法，运行后放入缓存中
     * org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
     * <p>
     * condition = "#id>1" id>1才进行缓存
     * unless = "#a0==2" 第一个参数为2不缓存
     *
     * @param id id
     * @return
     */
    // @Cacheable(cacheNames = {"emp"},key = "#root.methodName+'['+#id+']'") // 通过指定key的方式
    // @Cacheable(cacheNames = {"emp"}, keyGenerator = "myKeyGenerator", condition = "#id>1", unless = "#a0==2")
    @Cacheable(cacheNames = {"emp"},keyGenerator = "myKeyGenerator")
    // 通过指定keyGenerator的方式,id>1才进行缓存
    public Employee getEmp(Integer id) {
        System.out.println("查询" + id + "号的员工");
        Employee employee = employeeMapper.getById(id);
        return employee;
    }
    public void insert(Employee employee){
        employeeMapper.insertEmp(employee);
    }
    /**
     * @param employee
     * @return
     * @CachePut既调用方法，有更新缓存数据 修改了数据库数据后，同时更新缓存
     * 注意key要相同才能更新缓存
     * 运行时机：
     * 先调用目标方法，然后将结果缓存起来
     */
    @CachePut(value = "{emp}", keyGenerator = "myKeyGenerator2")
    public Employee updateEmp(Employee employee) {
        employeeMapper.updateEmp(employee);
        return employee;
    }

    /**
     * @param id
     * @CacheEvict缓存清除
     * allEntries = true 清除这个组件的所有缓存
     * beforeInvocation = false缓存的清除是否在方法之前执行，默认是方法执行之后来执行
     */
    @CacheEvict(value = "{emp}", keyGenerator = "myKeyGenerator2",allEntries = true,beforeInvocation = false)
    public void deleteEmp(Integer id) {
        employeeMapper.delete(id);
    }

    /**
     * Caching 定义复杂的缓存规则
     * @param name
     * @return
     */
    @Caching(
            cacheable = {
                    @Cacheable(value = "emp",keyGenerator = "myKeyGenerator2" )
            },
            put = {
                    @CachePut(value = "emp",key="#result.id"),
                    @CachePut(value = "emp",key = "@result.email")
            }
    )
    public Employee getEmpByLastName(String name){
        Employee employee = employeeMapper.selectByLastName(name);
        return employee;
    }
}
